package ch.fhnw.qualiscan.application.models;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import ch.fhnw.qualiscan.Config;
import ch.fhnw.qualiscan.core.Model;
import ch.fhnw.qualiscan.core.db.pojos.Answer;
import ch.fhnw.qualiscan.core.db.pojos.Domain;
import ch.fhnw.qualiscan.core.db.pojos.Language;
import ch.fhnw.qualiscan.core.db.pojos.Question;
import ch.fhnw.qualiscan.core.db.pojos.Role;
import ch.fhnw.qualiscan.core.db.pojos.RoleText;
import ch.fhnw.qualiscan.core.db.pojos.Survey;
import ch.fhnw.qualiscan.core.db.pojos.SurveyUserRole;
import ch.fhnw.qualiscan.core.db.pojos.User;
import ch.fhnw.qualiscan.core.helpers.EmailHelper;
import ch.fhnw.qualiscan.core.helpers.Hash;

/**
 * 
 * @author Kevin Mettenberger, Benjamin Groehbiel
 *
 */
public class UserModel extends Model {

	/**
	 * Checks if the login data is correct. 
	 * 
	 * @param email the users email-address.
	 * @param password the users password.
	 * @return returns a user object if correct, else null.
	 */
	public User login(String email, String password) { 
		//Close Connection > clear cache
		hibernateSession.getSession().close();
		Hash createHash = new Hash();
		String pwd = createHash.encryptPassword(password);
		
		User user = (User) hibernateSession.getSession().createQuery("from User as user where user.email = :email and user.password = :password")
		.setParameter("email", email)
		.setParameter("password", pwd)
		.uniqueResult();

		return user;
	}

	/**
	 * Adds a user to the database (only his email-address)
	 * @param email the users email-address
	 * @return if the user already exists this user is returned, else the added user
	 */
	public User addUser(String email) {
		User u = (User) hibernateSession.getSession().createQuery("from User as u where u.email = :email")
		.setParameter("email", email)
		.uniqueResult();
		
		if(u == null) {
			u = new User();
			u.setEmail(email);
		}
		
		return u;
	}
	
	/**
	 * Returns the user with the specified id.
	 * 
	 * @param id The id of the requested user object.
	 * @return Returns the user object if it exists in the database. Else null. 
	 */
	public User getUserById(int id) {
		User u = (User) hibernateSession.getSession().get(User.class, id);
		return u;
	}
	
	/**
	 * Gets a specific user by email
	 * @param email email address of user
	 * @return User object
	 */
	public User getUserByEmail(String email) {
		User u = (User) hibernateSession.getSession().createQuery("from User as u where u.email = :email")
		.setParameter("email", email)
		.uniqueResult();

		return u;
	}
	
	/**
	 * @return Returns all users except for system administrators.
	 */
	public List<User> getUsers() {
		Role admin = new Role();
		admin.setId(4);
		
		List<User> users = hibernateSession.getSession()
			.createQuery("from User as u WHERE u.role != :administrator")
			.setParameter("administrator", admin)
			.list();
		
		return users;
	}
	
	/**
	 * Updates a profile, except for the password
	 * @param user user, who would like to update its profile
	 * @return success 
	 */
	public boolean updateProfile(User user) {
		hibernateSession.getSession().merge(user);
		hibernateSession.closeSession();
		return true;
	}
	
	/**
	 * This function creates a new password for a user.
	 * @param userId id of the user
	 * @param password new password
	 * @return success
	 */
	public boolean updatePassword(int userId, String password, boolean sendEmail) {
		User u = (User) hibernateSession.getSession().createQuery("from User as u where u.id = :id")
		.setParameter("id", userId)
		.uniqueResult();
		
		if(sendEmail == true) {
			u.setPassword(password);
			EmailHelper mail = new EmailHelper();
			mail.sendPasswordMail(u);
		}
		
		Hash myHash = new Hash();
		String newPassword = myHash.encryptPassword(password);
		
		u.setPassword(newPassword);
		
		hibernateSession.getSession().update(u);
		hibernateSession.closeSession();
		return true;
	}
	
	/**
	 * Saves a user object
	 * @param user user you would like to save
	 * @return true
	 */
	public boolean updateUser(User user) {
		//assuming that all users can create surveys
		Role role = (Role) hibernateSession.getSession().get(Role.class, Config.COORDINATOR_ID);
		user.setRole(role);
		
		// encrypt password
		Hash createHash = new Hash();
		String pwd = createHash.encryptPassword(user.getPassword());
		user.setPassword(pwd);
		
		//add date
		user.setRegdate(new java.util.Date());
		hibernateSession.getSession().merge(user);
		hibernateSession.closeSession();
		return true;
	}
	

	/**
	 * Creates a new user.
	 * @param user
	 * @return true if able to store the user, else false.
	 */
	public boolean saveUser(User user) {
		
		// check if user exists
		User u = (User) hibernateSession.getSession().createQuery("from User as u where u.email = :email")
		.setParameter("email", user.getEmail())
		.uniqueResult();
		
		if(u == null) {
			//assuming that all users can create surveys -> see updateUser()
			Role role = (Role) hibernateSession.getSession().get(Role.class, Config.COORDINATOR_ID);
			user.setRole(role);
			
			//add date
			user.setRegdate(new java.util.Date());
			
			// send confirmation mail
			EmailHelper email = new EmailHelper();
			email.sendConfirmationMail(user);
			
			// encrypt password
			Hash createHash = new Hash();
			String pwd = createHash.encryptPassword(user.getPassword());
			user.setPassword(pwd);
			
			hibernateSession.getSession().save(user);
			hibernateSession.closeSession();
			return true;
		} else {
			return false;
		}
		
	}
	
	/**
	 * Returns the RoleText object for a specified role and language.
	 * @param role the role
	 * @param language the language
	 * @return a RoleText object.
	 */
	public RoleText getRoleText(Role role, Language language) {
		RoleText rt = (RoleText) hibernateSession.getSession().createQuery("from RoleText as rt where role = :role and language = :language")
		.setParameter("role", role)
		.setParameter("language", language)
		.uniqueResult();
		
		if (rt == null) {
			Language l = new Language();
			l.setId(Config.ENGLISH_ID);
			
			rt = (RoleText) hibernateSession.getSession().createQuery("from RoleText as rt where role = :role and language = :language")
			.setParameter("role", role)
			.setParameter("language", l)
			.uniqueResult();
		}
		return rt;
	}
	
	/**
	 * Returns a list of available roles
	 * @return list containing role objects
	 */
	public List<Role> getRoles() {
		List<Role> roleList = hibernateSession.getSession().createQuery("from Role").list();
		return roleList;
	}
	
	/**
	 * Checks if a email address is already being used of another user in the system.
	 * @param email email address
	 * @return true if email is not being used, false if email is being used
	 */
	public boolean isEmailValid(String email) {
		
		User userCheck = (User) hibernateSession.getSession().createQuery("from User as u where u.email = :email")
		.setParameter("email", email)
		.uniqueResult();
		
		if(userCheck == null)
			return true;
		
		User u = (User) hibernateSession.getSession().createQuery("from User as u where u.email = :email AND u.id != :id")
		.setParameter("email", email)
		.setParameter("id", userCheck.getId())
		.uniqueResult();
		
		if(u != null)
			return false;
		else
			return true;
	}
	
	/**
	 * Get the users, who haven't completed the survey.
	 * @param survey the survey
	 * @param user the loggedIn user (probably coordinator)
	 * @return list containing User objects
	 */
	public List<SurveyUserRole> getUsersNotCompleted(Survey survey, User user) {
		
		List<SurveyUserRole> surList = new ArrayList<SurveyUserRole>();
		@SuppressWarnings("unchecked")
		Role r = new Role();
		r.setId(Config.EXPERT_ID);
		List<SurveyUserRole> provSurList = hibernateSession.getSession().createQuery("from SurveyUserRole where survey = :survey and user != :user and role != :role")
		.setParameter("survey", survey)
		.setParameter("user", user)
		.setParameter("role", r)
		.list();
		
		boolean doBreak = false;
		for(int i = 0; i < provSurList.size(); i++) {
			Iterator<Domain> domainIt = survey.getDomains().iterator();
			while(domainIt.hasNext()) {
				if (doBreak == true) {
					doBreak = false;
					break;
				}
				Iterator<Question> questionIt = domainIt.next().getQuestions().iterator();
				while(questionIt.hasNext()) {
					Question q = questionIt.next();
					if (q.getSubquestions().size() == 0) {
						Answer answer = (Answer) hibernateSession.getSession().createQuery("from Answer where question = :question and sur = :sur")
						.setParameter("question", q)
						.setParameter("sur", provSurList.get(i))
						.uniqueResult();
						
						if (answer == null) {
							surList.add(provSurList.get(i));
							doBreak = true;
							break;
						}
					}
					
				}
			}
		}
		return surList;
	}
	
}
